def add_getattr_setattr(cls):
  _attr = {}
  def __setattr__(self, name, value):
    self._attr[name] = value
    
  def __getattr__(self, name):
    if name not in self._attr:
      raise AttributeError
    return self._attr[name]
  cls._attr = _attr
  cls.__setattr__ = __setattr__
  cls.__getattr__ = __getattr__
  return cls


class Test1():
  print('Test1')
  class A():
      pass

  a = A()
  # print(a.test) # raise AttributeError

class Test2():
  print('Test2')
  class A():
    def __getattr__(self, name):
      print('__getattr__')
      print(name)
      # raise AttributeError() 
      pass

  a = A()
  print(a.test)

class Test3():
  print('Test3')
  class A():
    def __getattr__(self, name):
      print('__getattr__')
      print(name)
      return None

  a = A()
  print(a.test)

class Test4():
  print('Test4')
  class A():
    def __init__(self):
      print('__init__')
      self.age = 11

    def __getattr__(self, name):
      '''
      这个方法只有在 不存在的属性时，才调用该方法。 
      '''
      print('__getattr__')
      print(name)
      return None

  a = A()
  print(a.age)
  print(a.test)

class Test5():
  print('Test5')
  class A():
    def __init__(self):
      print('__init__')
      self.age = 11

    def __getattr__(self, name):
      '''
      这个方法只有在 不存在的属性时，才调用该方法。 
      '''
      print('__getattr__')
      print(name)
      return None

    def __getattribute__(self, __name: str):
      print('__getattribute__')
      print(__name)
      return None

  a = A()
  # print(a.age)
  print(a.test) 
  # 最终调用的是 getattrbute, 有没有这个属性，都会调用 getattrbute 这个属性，而且，只要有 getatrbute 这个方法，能够分配属性值，getattr 这个属性就不会调用了。
  # getattr 方法是 getattribute 的 上层方法

class Test6():
  '''
    实用的例子 ,计算属性被调用了几次。
  '''
  print('Test6')
  class A():
    def __init__(self):
      self.times = 0
      self.name = 'wyl350'
    def __getattr__(self, name):
      print('__getattr__')
      return None
    def __getattribute__(self, __name: str):
      print('__getattribute__')
      if __name == 'name':
        self.times += 1 # 特别特别注意，这是个递归。
      return super().__getattribute__(__name)
  
  a = A()
  print(a.name)
  print(a.name)
  print(a.times)
  print(a.www)

class Test7():
  print('Test7')

  class A():
    def __init__(self) -> None:
      self.data = 'abc'
      self.counter = 0
    
    def __setattr__(self, __name: str, __value) -> None:
      print('__setattr__')
      print(__name, __value)
      super().__setattr__(__name, __value)
      pass

  a= A()
  print(a)
  print(a.data)
  print(type(a.data))
  print(a.counter)
  print(type(a.counter))

class Test8():
  '''
      这是一个非常实用的例子，就是旁置出一个属性对象，将属性放进去，把属性拿出来。 
      而且，这里的这个属性拦截器是 类属性的层次，多个实例对象是共享的。
  '''
  print('Test8')
  class A():
    _attr = {}
    def __init__(self):
      self.data = 'abc'  
  
    def __setattr__(self, name, value):
      print('__setattr__')
      print(name,value)
      self._attr[name] = value
      print(self._attr)
    
    def __getattr__(self, name):
      print('__getattr__')
      if name not in self._attr:
        raise AttributeError
      return self._attr[name]

  a1 = A() # 初始化的时候，就调用 setattr 这个方法
  print(a1.data)
  a2 = A()
  a1.data = 'xyz'
  print(a2.data)

class Test9():
  print('Test9')
  @add_getattr_setattr
  class A():
    def __init__(self):
      self.data = 'abcd'
    
  a1 = A()
  a2 = A()
  print(a1.data)
  a1.data = 'xyz'
  print(a2.data)
  print(a1.data)
  a2.data = 'wyl350'
  print(a2.data)
  print(a1.data)
  # 只要任何一个对象 改变公共属性，公共属性的值都会发生变化

class Test10():
  print('Test10')

  class A():
    def __init__(self):
      self.data = 'abc'

    def __delattr__(self, __name: str) -> None:
      print('__delattr__')
      print(__name)
  
  a1 = A() # 对象的创建和消失， __delattr__ 这个方法是不会调用的
  del a1.data
  print(a1.data) # 方法没有处理，所以并没有真正删除

class Test11():
  print('Test11')
  class A():
    def __init__(self):
      self.data = 'abc'

    def __delattr__(self, __name: str) -> None:
      print('__delattr__')
      print(__name)
      super().__delattr__(__name)
  
    def __getattr__(self, name):
      print('name','test_name')
      return None
  a1 = A() # 对象的创建和消失， __delattr__ 这个方法是不会调用的
  del a1.data
  print(a1.data) # 方法没有处理，所以并没有真正删除

class Test112():
  print('Test12')
  class A():
    def __init__(self):
      self.data = 'abc'
  a1 = A()
  print(a1)
  print(dir(a1))

class Test13():
  print('Test13')
  class A():
    def __init__(self):
      self.data = 'abc'
    def __dir__(self):
      return []
    
  a1 = A()
  print(a1)
  print(dir(a1))

class Test14():
  print('Test14')
  class A():
    def __init__(self):
      self.data = 'abc'
    def __dir__(self):
      list1 = super().__dir__()
      return [el for el in list1 if not el.startswith('__')]
    
  a1 = A()
  print(a1)
  print(dir(a1))
